﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace Xceed.Silverlight.DataGrid
{
  internal class ColumnPanel
  {
    public ColumnPanel( IColumnPanelOwner columnPanelOwner )
    {
      m_leftFixedSection = new ColumnInnerPanel();
      m_rightFixedSection = new ColumnInnerPanel();
      m_scrollingSection = new ColumnInnerPanel();
      m_children = new List<ColumnContainer>();
      m_childrenManager = new ChildrenManager<ColumnContainer>( m_children.Add, (c) => m_children.Remove(c) );
      m_columnPanelOwner = columnPanelOwner;
    }

    internal IEnumerable<ColumnContainer> RenderedColumns
    {
      get
      {
        return this.ScrollableLayout.Elements
          .Union(m_leftFixedSection.Layout.Elements)
          .Union(m_rightFixedSection.Layout.Elements)
          .Cast<ColumnContainer>();
      }
    }

    internal IEnumerable<ColumnContainer> AllColumns
    {
      get
      {
        return this.ChildrenManager.AllElements;
      }
    }

    internal ChildrenManager<ColumnContainer> ChildrenManager
    {
      get
      {
        return m_childrenManager;
      }
    }

    internal ILayoutContainer LeftFixedSection
    {
      get
      {
        return m_leftFixedSection;
      }
    }

    internal ILayoutContainer RightFixedSection
    {
      get
      {
        return m_rightFixedSection;
      }
    }

    internal ILayoutContainer ScrollableSection
    {
      get
      {
        return m_scrollingSection;
      }
    }

    internal ILayout ScrollableLayout
    {
      get
      {
        return m_scrollingSection.Layout;
      }
    }

    internal double ColumnElementsIndent { get; set; }

    public void CommitColumns()
    {
      m_leftFixedSection.CommitLayout();
      m_rightFixedSection.CommitLayout();
      m_scrollingSection.CommitLayout();
    }

    public void RollBackColumns()
    {
      this.ApplyLayoutUpdateResult(
        m_leftFixedSection.RollbackLayout(),
        m_scrollingSection.RollbackLayout(),
        m_rightFixedSection.RollbackLayout() );
    }

    public void SetColumns( ILayout leftFixedLayout, ILayout scrollableColumns, ILayout rightFixedColumns )
    {
      this.ApplyLayoutUpdateResult(
        m_leftFixedSection.SetLayout( leftFixedLayout ),
        m_scrollingSection.SetLayout( scrollableColumns ),
        m_rightFixedSection.SetLayout( rightFixedColumns ) );
    }

    private void ApplyLayoutUpdateResult( 
      LayoutUpdateResult left, 
      LayoutUpdateResult scrollable, 
      LayoutUpdateResult right )
    {
      IEnumerable<ILayoutElement> added = scrollable.AddedElements
        .Union( right.AddedElements )
        .Union( left.AddedElements );

      IEnumerable<ILayoutElement> removed = scrollable.RemovedElements
        .Union( right.RemovedElements )
        .Union( left.RemovedElements );

      foreach( ILayoutElement element in removed.Except(added) )
      {
        this.ReleaseElement( element );
      }

      foreach( ILayoutElement element in added.Except(removed) )
      {
        this.AddElement( element );
      }
    }

    public ViewportInfo GetViewportInfo( Size availableSize )
    {
      Size leftMeasure = m_leftFixedSection.Layout.DesiredSize;
      Size rightMeasure = m_rightFixedSection.Layout.DesiredSize;

      double availableWidth = availableSize.Width;
      availableWidth -= this.ColumnElementsIndent;
      availableWidth -= ( leftMeasure.Width + rightMeasure.Width );
      availableSize.Width = Math.Max( 0d, availableWidth );
      return m_scrollingSection.Layout.GetViewportInfo( new Rect( 0, 0, availableSize.Width, availableSize.Height ) );
    }

    public Size Measure( Size availableSize )
    {
      availableSize.Width = Math.Max(0d, availableSize.Width - this.ColumnElementsIndent);
      Size leftMeasure = m_leftFixedSection.Layout.MeasurePanel( availableSize );
      Size rightMeasure = m_rightFixedSection.Layout.MeasurePanel( availableSize );

      availableSize.Width -= (leftMeasure.Width + rightMeasure.Width);
      availableSize.Width = Math.Max(0d, availableSize.Width);
      Size scrollableMeasure =  m_scrollingSection.Layout.MeasurePanel( availableSize );

      scrollableMeasure.Width += ( leftMeasure.Width + rightMeasure.Width );
      scrollableMeasure.Width += this.ColumnElementsIndent;

      return scrollableMeasure;
    }


    public void Arrange(Size finalSize, LayoutParameters parameters )
    {

      Size leftDesiredSize = m_leftFixedSection.Layout.DesiredSize;
      Size rightDesiredSize = m_rightFixedSection.Layout.DesiredSize;

      m_leftFixedSection.Layout.ArrangePanel( leftDesiredSize, parameters );
      m_rightFixedSection.Layout.ArrangePanel( rightDesiredSize, parameters );

      double finalWidth = finalSize.Width;
      finalWidth -= this.ColumnElementsIndent;
      finalWidth -= leftDesiredSize.Width;
      finalWidth -= rightDesiredSize.Width;
      finalSize.Width = Math.Max( 0d, finalWidth );
      finalSize.Height = 0d;

      m_scrollingSection.Layout.ArrangePanel( finalSize, parameters );
    }

    public void EndTransition()
    {
      m_leftFixedSection.EndTransition();
      m_rightFixedSection.EndTransition();
      m_scrollingSection.EndTransition();
    }

    internal bool TryGetElement( Column column, out ColumnContainer columnContainer )
    {
      return this.ChildrenManager.TryGetElement( column, out columnContainer );
    }

    internal bool TryGetRecycledElement( Column column, Column exceptionColumn, out ColumnContainer columnContainer )
    {
      return this.ChildrenManager.TryGetRecycledElement( column, exceptionColumn, out columnContainer );
    }

    internal void RemoveElement( ColumnContainer element )
    {
      element.LastArrangeRect = null;
      this.ChildrenManager.Remove( element );
    }

    internal void RecycleElement( ColumnContainer element )
    {
      element.LastArrangeRect = null;
      this.ChildrenManager.Recycle( element );
    }

    internal void InvalidatePanelMeasure()
    {
      m_columnPanelOwner.InvalidatePanelMeasure();
    }

    internal void InvalidatePanelArrange()
    {
      m_columnPanelOwner.InvalidatePanelArrange();
    }

    internal void AddElement( ILayoutElement element )
    {
      ColumnContainer column = ( ColumnContainer )element;
      this.ChildrenManager.Add( column );
      m_columnPanelOwner.OnColumnAdded( column );
    }

    internal void ReleaseElement( ILayoutElement element )
    {
      ColumnContainer column = ( ColumnContainer )element;
      // We do not remove the column yet. We let the DataGrid handle the
      // recycling logic.
      m_columnPanelOwner.OnColumnRemoved( column );
    }

    private ChildrenManager<ColumnContainer> m_childrenManager;//null
    private IColumnPanelOwner m_columnPanelOwner;
    private List<ColumnContainer> m_children;

    private ColumnInnerPanel m_leftFixedSection;
    private ColumnInnerPanel m_rightFixedSection;
    private ColumnInnerPanel m_scrollingSection;
  }
}
